"""
Redis缓存服务
提供分布式缓存功能，集成Redis作为缓存后端
"""

import time
import json
import hashlib
import pickle
from typing import Any, Optional, Dict, Union, cast
import redis
from redis.exceptions import RedisError, ConnectionError, TimeoutError
from src.config.settings import settings
from src.utils.logging import get_logger

logger = get_logger(__name__)


class RedisCacheService:
    """Redis缓存服务类"""
    
    def __init__(self, 
                 host: Optional[str] = None,
                 port: Optional[int] = None,
                 db: Optional[int] = None,
                 password: Optional[str] = None,
                 default_ttl: Optional[int] = None):
        """
        初始化Redis缓存服务
        
        Args:
            host: Redis服务器地址
            port: Redis服务器端口
            db: Redis数据库编号
            password: Redis密码
            default_ttl: 默认缓存存活时间（秒）
        """
        # 正确访问Redis配置
        self.host = host or getattr(settings, 'REDIS_HOST', 'localhost')
        self.port = port or getattr(settings, 'REDIS_PORT', 6379)
        self.db = db or getattr(settings, 'REDIS_DB', 0)
        self.password = password or getattr(settings, 'REDIS_PASSWORD', None)
        self.default_ttl = default_ttl or getattr(settings, 'CACHE_DEFAULT_TTL', 3600)
        
        # 初始化Redis连接池
        try:
            self.redis_client = redis.Redis(
                host=self.host,
                port=self.port,
                db=self.db,
                password=self.password,
                decode_responses=False,  # 使用pickle序列化，不自动解码
                socket_connect_timeout=5,
                socket_timeout=5,
                retry_on_timeout=True,
                health_check_interval=30,
                socket_keepalive=True
            )
            
            # 测试连接
            self.redis_client.ping()
            logger.info(f"Redis缓存服务连接成功: {self.host}:{self.port}/{self.db}")
        except Exception as e:
            logger.error(f"Redis缓存服务连接失败: {e}")
            self.redis_client = None
    
    def _serialize(self, value: Any) -> bytes:
        """序列化值为字节"""
        try:
            return pickle.dumps(value)
        except Exception as e:
            logger.error(f"序列化失败: {e}")
            raise
    
    def _deserialize(self, value: bytes) -> Any:
        """反序列化字节为值"""
        try:
            return pickle.loads(value)
        except Exception as e:
            logger.error(f"反序列化失败: {e}")
            raise
    
    def _get_cache_key(self, key: str) -> str:
        """生成安全的缓存键"""
        # 为避免键冲突，添加前缀
        return f"cache:{key}"
    
    def set(self, key: str, value: Any, ttl: Optional[int] = None) -> bool:
        """
        设置缓存
        
        Args:
            key: 缓存键
            value: 缓存值
            ttl: 存活时间（秒），None使用默认值
            
        Returns:
            是否设置成功
        """
        if not self.redis_client:
            logger.warning("Redis客户端未初始化，无法设置缓存")
            return False
            
        try:
            cache_key = self._get_cache_key(key)
            serialized_value = self._serialize(value)
            expires_at = time.time() + (ttl or self.default_ttl)
            
            # 使用hash存储值和过期时间
            cache_data = {
                b'value': serialized_value,
                b'expires_at': str(expires_at).encode('utf-8'),
                b'created_at': str(time.time()).encode('utf-8')
            }
            
            # 使用Redis的hash结构存储缓存数据
            result = self.redis_client.hset(cache_key, mapping=cache_data)
            
            # 设置过期时间
            self.redis_client.expire(cache_key, ttl or self.default_ttl)
            
            logger.debug(f"Redis缓存设置成功: {key}")
            return True
            
        except (ConnectionError, TimeoutError) as e:
            logger.warning(f"Redis连接错误: {e}")
            return False
        except Exception as e:
            logger.error(f"设置Redis缓存失败 {key}: {e}")
            return False
    
    def get(self, key: str, default: Any = None) -> Any:
        """
        获取缓存值
        
        Args:
            key: 缓存键
            default: 缓存不存在时的默认值
            
        Returns:
            缓存值或默认值
        """
        if not self.redis_client:
            logger.warning("Redis客户端未初始化，无法获取缓存")
            return default
            
        try:
            cache_key = self._get_cache_key(key)
            
            # 检查键是否存在
            if not self.redis_client.exists(cache_key):
                logger.debug(f"Redis缓存未找到: {key}")
                return default
            
            # 获取过期时间
            expires_at_bytes = self.redis_client.hget(cache_key, 'expires_at')
            
            # 如果没有过期时间，认为缓存无效
            if expires_at_bytes is None:
                self.redis_client.delete(cache_key)
                return default
                
            # 如果是bytes类型则需要解码，否则直接转换
            if isinstance(expires_at_bytes, bytes):
                expires_at = float(expires_at_bytes.decode('utf-8'))
            else:
                # 如果expires_at_bytes是Awaitable对象或其他非bytes类型，删除缓存
                logger.warning(f"Redis缓存expires_at格式不正确: {key}")
                self.redis_client.delete(cache_key)
                return default
            
            # 检查是否过期
            if time.time() >= expires_at:
                # 缓存已过期，删除
                self.redis_client.delete(cache_key)
                return default
            
            # 获取缓存值
            value_bytes = self.redis_client.hget(cache_key, 'value')
            if value_bytes is None:
                return default
                
            if isinstance(value_bytes, bytes):
                return self._deserialize(value_bytes)
            else:
                # 如果value_bytes是Awaitable对象或其他非bytes类型，删除缓存
                logger.warning(f"Redis缓存value格式不正确: {key}")
                self.redis_client.delete(cache_key)
                return default
            
        except (ConnectionError, TimeoutError) as e:
            logger.warning(f"Redis连接错误: {e}")
            return default
        except Exception as e:
            logger.error(f"获取Redis缓存失败 {key}: {e}")
            return default
